#ifndef __ARREGLO_REDIMENSIONABLE_H__
#define __ARREGLO_REDIMENSIONABLE_H__

#ifndef NULL
#define NULL 0
#endif

#include <iostream>
#include <cassert>
#include "defines.h"

using namespace std;

template<typename T>
class Arreglo_redimensionable{
public:
	Arreglo_redimensionable(int); // el parametro es el largo inicial del arreglo
	Arreglo_redimensionable(); //constructor por defecto
	~Arreglo_redimensionable();
	nat tam() const;
	void agregar(const T&,nat);
	bool definido(nat) const;
	const T& iesimo(nat) const;
	void extender();

    void mostrar(ostream&) const;

    Arreglo_redimensionable& operator=(const Arreglo_redimensionable&); // funciona por copia
    bool operator==(const Arreglo_redimensionable&) const;

private:
	struct tupla{
		T elem;
		bool definido;
	};
	tupla* arr;
	int tamanio;

	void vaciar();
	void copiar(const Arreglo_redimensionable&);
};

template<typename T>
Arreglo_redimensionable<T>::Arreglo_redimensionable(int cant):tamanio(cant),arr(new tupla[cant]){}

template<typename T>
Arreglo_redimensionable<T>::Arreglo_redimensionable():tamanio(0),arr(NULL){} //constructor por defecto

template<typename T>
Arreglo_redimensionable<T>::~Arreglo_redimensionable(){
	vaciar();
}

template<typename T>
nat Arreglo_redimensionable<T>::tam() const{
	return tamanio;
}

template<typename T>
void Arreglo_redimensionable<T>::agregar(const T& b, nat i){
    assert(i<tamanio);
	arr[i].elem = b;
	arr[i].definido=true;
}

template<typename T>
bool Arreglo_redimensionable<T>::definido(nat i) const{
    assert(i<tamanio);
	return arr[i].definido;
}

template<typename T>
const T& Arreglo_redimensionable<T>::iesimo(nat i) const{
    assert(i<tamanio);
    assert(definido(i));
	return arr[i].elem;
}

template<typename T>
void Arreglo_redimensionable<T>::extender(){
	tupla* brr=new tupla[tamanio+1];
	for(int i=0;i<tamanio;i++){
		brr[i]=arr[i];
	}
	delete[] arr;
	arr = brr;
	arr[tamanio].definido = false;
	tamanio++;
}

template<typename T>
Arreglo_redimensionable<T>& Arreglo_redimensionable<T>::operator=(const Arreglo_redimensionable<T>& a){
    vaciar();
    copiar(a);
}

template<typename T>
bool Arreglo_redimensionable<T>::operator==(const Arreglo_redimensionable<T>& a) const{
    if(a.tam()!=tamanio) return false;
    for(int i=0;i<tamanio;i++){
        if(a.definido(i)!=definido(i)) return false;
        if(definido(i) && (a.iesimo(i)!=iesimo(i))) return false;
    }
    return true;
}

template<typename T>
void Arreglo_redimensionable<T>::mostrar(ostream& os) const{
    os << '[';
    for(int i=0;i<tamanio;i++){
        if(arr[i].definido){
            if(i!=0) os << ',';
            os << arr[i].elem;
        }
    }
    os << ']';
}

template<typename T>
void Arreglo_redimensionable<T>::vaciar(){
    delete[] arr;
    tamanio = 0;
}

template<typename T>
void Arreglo_redimensionable<T>::copiar(const Arreglo_redimensionable& a){
    for(int i=0;i<a.tam();i++){
        extender();
        if(a.definido(i)) agregar(a.iesimo(i),i);
    }
}

template<class T>
ostream& operator<<(ostream& out, const Arreglo_redimensionable<T>& a) {
	a.mostrar(out);
	return out;
}

#endif
